package WarTug;

//This includes everything in the server side game logic

import java.io.IOException;
import java.util.ArrayList;

import javax.management.timer.Timer;

import com.esotericsoftware.kryonet.Connection;
import com.esotericsoftware.kryonet.Listener;
import com.esotericsoftware.kryonet.Server;

import WarTug.Network.WarTugConnection;
import WarTug.Network.RegisterName;

public class WarTugServer implements Runnable {
	Server server; //Server listening for client connections
	public boolean end = false;
	public int numPlayers;
	public long start = 0;
	private final long tickLength = 8000;
	private long currTickTime = 0;
	private ArrayList<SimpleUnit> allUnits = new ArrayList<SimpleUnit>(); //Holds all units tracked server side
	private Player[] players = new Player[2];
	private int currWave = 0;

    public WarTugServer () throws IOException {
    	//Set-up Server here
    	server = new Server() {
            protected Connection newConnection () {
                    // By providing our own connection implementation, we can store per
                    // connection state without a connection ID to state look up.
                    return new WarTugConnection();
            }
    	};
    	
    	// For consistency, the classes to be sent over the network are
        // registered by the same method for both the client and server.
        Network.register(server);
        
        server.addListener(new Listener() {
            public void received (Connection c, Object object) {
                // We know all connections for this server are actually ChatConnections.
            	WarTugConnection connection = (WarTugConnection)c;

                if (object instanceof RegisterName) {
                	if (numPlayers==2) return; //Only let two players on server
                    if (connection.name != null) return; // Ignore the object if the name is invalid.
                    String name = ((RegisterName)object).name;
                    if (name == null) return; //Ignore if no name
                    name = name.trim();
                    if (name.length() == 0) return;
                    // Store the name on the connection.
                    connection.name = name;
                    connection.playerNum = numPlayers; //Give player a player number based upon order of connection
                    
                    players[numPlayers] = new Player(numPlayers);
                    
                    synchronized(allUnits) {
                    	allUnits.add(new UnitTreeOfLife(numPlayers));
                    }
                    
                    //increment number of players
                    numPlayers++;
                    return;
                }
                if (object instanceof SimpleUnit) {
                	//Don't know if this will ever happen...
                    return;
                }
                if (object instanceof Garden) {
                	Garden g = (Garden) object;
                	if (g!=null) {
                		synchronized (players) {
                			players[g.playerNumber].garden = g;
                		}
                	}
                }
                if (object instanceof Network.ClearRequest) {
                	if (object != null) {
                		synchronized(allUnits) {
                			for (SimpleUnit u : allUnits) {
                				if (u.unitType != UnitType.treeOfLife)
                					u.dealDamage(1000);
                			}
                		}
                	}
                }
            }
            public void disconnected (Connection c) {
            	//Need to decide what to do if a client dcs
            }
        });

        server.bind(Network.port); //Binds all of the server listeners to a port
        server.start();
    }
    
    public void run() {
    	//Here is where we have the main server game loop
    	while (!end && numPlayers<2)
    	{
    		Thread.yield();
    	}
		start = System.currentTimeMillis();
		while (!end){
    		long delta = System.currentTimeMillis() - start;
    		start = System.currentTimeMillis();
    		try {
				Thread.sleep(100); //TODO:Perhaps make new function to mimic the effects of Display.sync(100), it might have to use time stamps, though...
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    		
    		if (isUnitSpawnTick(delta)) {
    			spawnUnits();
    			if (currWave % 3 == 0) {
    				server.sendToAllTCP(new Integer(1));
    			}
    			synchronized (players) {
    				for (Player p : players)
    				{
    					server.sendToAllTCP(p.garden);
    				}
    			}
    		}

    		//Send units to the clients, TODO:Maybe have this in unit manager.  
    		//Pro: looping through units is done in unit manager, cleaner code.  
    		//Con: Must pass server to unit manager
    		ArrayList<SimpleUnit> toRemove = new ArrayList<SimpleUnit>();
    		int winner = -1;
    		boolean gameOver = false;
    		
    		synchronized(allUnits) {
    			for (SimpleUnit unit : allUnits){
    				unit.move(delta);
    			}
	    		for (SimpleUnit unit : allUnits){
	    			unit.doLogic(delta, allUnits);
	    		}
	    		for (SimpleUnit unit : allUnits) {
	    			if (unit.isDead) {
	    				toRemove.add(unit);
	    			}
	    			server.sendToAllTCP(unit);
	    		}
	    		for (SimpleUnit unit : toRemove) {
	    			if (unit.unitType == UnitType.treeOfLife) {
	    				gameOver = true;
	    				if (winner == -1) {
	    					if (unit.unitAllegiance == 0) {
	    						winner = 1;
	    					} else {
	    						winner = 0;
	    					}
	    				} else {
	    					winner = -1;
	    				}
	    			}
	    			allUnits.remove(unit);
	    		}
    		}
    		
    		//Check if the game is over
    		if (gameOver) {
    			Network.sendEndGameStats(server, winner);
    			break;
    		}
    	}
    }

	private boolean isUnitSpawnTick(long delta) {
		currTickTime += delta;
		if (currTickTime >= tickLength) {
			currTickTime -= tickLength;
			currWave++;
			return true;
		} else {
			return false;
		}
	}
	
	private void spawnUnits() { 
		synchronized (players) {
			for (Player p : players) {
				addAllUnits(p.garden.spawnUnits());
			}
		}
	}
	
	private void addAllUnits(ArrayList<SimpleUnit> newUnits) {
		for (SimpleUnit s : newUnits) {
			allUnits.add(s);
		}
	}
}
